package com.nx.platform.es.bean.modle.query;

import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.nx.platform.es.bean.dto.RequestContext;
import com.nx.platform.es.biz.wrapper.parser.StatementOperator;
import org.apache.commons.collections4.MapUtils;
import org.elasticsearch.common.Strings;
import org.elasticsearch.index.query.DisMaxQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;

import java.util.List;
import java.util.regex.Pattern;

/**
 * @author
 * @since 2016年10月15日
 */
public class NumberDisMaxHandler implements QueryFieldHandler {

    private static final Pattern FORMAT_SINGLE = Pattern.compile("^-?\\d+$");
    private static final Pattern FORMAT_SOME = Pattern.compile("^-?\\d+(?:\\|-?\\d+)+$");
    private static final Pattern FORMAT_RANGE = Pattern.compile("^(-?\\d+|\\*)_(-?\\d+|\\*)$");

    @Override
    public void handle(ImmutableMap<String, ?> fieldConfig, RequestContext context, String fieldFace,
                       StatementOperator operator, String fieldValue, ListMultimap<Boolean, QueryBuilder> queryBuilders) {
        String defaultValue = MapUtils.getString(fieldConfig, "default");
        if (Strings.isNullOrEmpty(fieldValue)) {
            fieldValue = defaultValue;
        }
        // 空值
        if (Strings.isNullOrEmpty(fieldValue)) {
            return;
        }
        //
        String face = MapUtils.getString(fieldConfig, "face");
        String fieldName = MapUtils.getString(fieldConfig, "fieldName");
        Preconditions.checkState(!Strings.isNullOrEmpty(fieldName));
        final String clazz = MapUtils.getString(fieldConfig, "class");
        // 多个值
        if (FORMAT_SOME.matcher(fieldValue).matches()) {
            List<Comparable<? extends Number>> set = Lists.newArrayList(FluentIterable
                    .from(Splitter.on('|').omitEmptyStrings().trimResults().split(fieldValue))
                    .transform(input -> parse(clazz, input)).filter(Predicates.notNull()).toSortedSet(Ordering.natural()));
            // 记录Query信息(Trace)
            context.getTrace().append("&").append(fieldFace)
                    .append(operator.getSymbol()).append(Joiner.on('|').join(set));

            DisMaxQueryBuilder disMax = new DisMaxQueryBuilder();
            Float tie_breaker = Floats.tryParse(MapUtils.getString(fieldConfig, "_tie_breaker"));
            disMax.tieBreaker((null != tie_breaker) ? tie_breaker : 1);
            set.forEach(x -> {
                disMax.add(QueryBuilders.termQuery(fieldName, x));
            });
            queryBuilders.put(operator.isSign(), disMax);
            return;
        }
        // 记录Query信息(Trace)
        context.getTrace().append("&").append(fieldFace).append(operator.getSymbol()).append(fieldValue);
        //
        // 单个值
        if (FORMAT_SINGLE.matcher(fieldValue).matches()) {
            queryBuilders.put(operator.isSign(),
                    QueryBuilders.termQuery(fieldName, parse(clazz, fieldValue)));
            return;
        }

        throw new IllegalArgumentException("format incorrect: field=" + face + ", value=" + fieldValue);
    }

    protected Comparable<? extends Number> parse(String clazz, String str) {
        Optional<Parsers> parser = Enums.getIfPresent(Parsers.class, clazz);
        if (parser.isPresent()) {
            return parser.get().parse(str);
        } else {
            return Parsers.Long.parse(str);
        }
    }

    static enum Parsers {
        Byte {
            @Override
            Comparable<? extends Number> parse(String str) {
                try {
                    return java.lang.Byte.parseByte(str);
                } catch (NumberFormatException nfe) {
                    return null;
                }
            }
        },
        Short {
            @Override
            Comparable<? extends Number> parse(String str) {
                try {
                    return java.lang.Short.parseShort(str);
                } catch (NumberFormatException nfe) {
                    return null;
                }
            }
        },
        Integer {
            @Override
            Comparable<? extends Number> parse(String str) {
                return Ints.tryParse(str);
            }
        },
        Long {
            @Override
            Comparable<? extends Number> parse(String str) {
                return Longs.tryParse(str);
            }
        };

        abstract Comparable<? extends Number> parse(String str);

    }

}
